package com.vansoftwaredesign.widgetlibrary;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;


public class CopyOfJoyStickWidget extends View {

	//create an instance of the config object
	WidgetConfig myConfig = new WidgetConfig();
	//set if we are in debug mode
	boolean inDebug = myConfig.inDebugMode();
	
	private final static String TAG = "JoyStickWidget";
	
	//booleans to determine if we drawn the constraint
	private boolean drawConstraintRect   = false;
	private boolean drawConstraintCircle = false;
	
	//pointer variables and on touch bits 
	private static final int INVALID_POINTER_ID = -1;
	private int pointerId = INVALID_POINTER_ID;
	
	
	//save session variables
	private int topDim;
	private int bottomDim;
	private int rightDim;
	private int leftDim;
	private int measuredHeight;
	private int measuredWidth;
	private float backgroundCircleRadius;
	private float backgroundCircleX;
	private float backgroundCircleY;
	private float joyStickX = -1;
	private float joyStickY = -1;
	private float joyStickRadius = 35f;
	
	//Set up some drawing defaults
	private float rimWidth = 10.0f;
	private int   backgroundAlpha = 160;
	
	//Paint & Drawing Objects for the canvas
	private Paint constraintPaint = new  Paint();
	private Rect  constraintRect  = new Rect();
	private Paint targetAreaBackGroundPaint = new Paint();
	private Paint rimPaintInner = new Paint();
	private LinearGradient rimShaderInner;
	private Paint rimPaintOuter = new Paint();
	private LinearGradient rimShaderOuter;
	private Paint rimPaintOuterLine = new Paint();
	
	
	//joystick handle
	private Paint joyStickHandlePaint = new Paint();
	private RadialGradient joyStickShader;
	
	
	
	
	
	
	
	public CopyOfJoyStickWidget(Context context) {
		super(context);
		setDefaults(context,null);
	}

	public CopyOfJoyStickWidget(Context context, AttributeSet attrs) {
		super(context, attrs);
		setDefaults(context,attrs);
	}

	public CopyOfJoyStickWidget(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		setDefaults(context,attrs);
	}
	
	//initialize the layout based on attributes set in XML
	private void setDefaults(Context context, AttributeSet attrs) {
		
		if(context != null & attrs != null){
			
		}
		
		initView();
		
	}

	private void initView() {
		 
		if(inDebug) { Log.d(TAG,"In initView"); } 
		
		if(drawConstraintRect||drawConstraintCircle){
			constraintPaint.setAntiAlias(true);
			constraintPaint.setColor(Color.RED);                
			constraintPaint.setStyle(Paint.Style.STROKE); 
			constraintPaint.setStrokeWidth(2);
		}
		
		
		//target Area
		targetAreaBackGroundPaint.setAntiAlias(true);
		targetAreaBackGroundPaint.setColor(Color.BLACK);
		targetAreaBackGroundPaint.setAlpha(backgroundAlpha);
		
		joyStickHandlePaint.setAntiAlias(true);
		joyStickHandlePaint.setColor(Color.BLUE);
		joyStickHandlePaint.setAlpha(200);
		
		
	}

	@Override
	protected void onDraw(Canvas canvas) {
		
		super.onDraw(canvas);
		
		if(inDebug) { Log.d(TAG,"In onDraw " + getWidth() + " " + getHeight()); }
		
		//Rectangle that allows you to visualize the size and position of the view
		if(drawConstraintRect){
			constraintRect.set(0, 0, measuredWidth, measuredHeight);
			canvas.drawRect(constraintRect, constraintPaint);
		}
		
		backgroundCircleY      = measuredHeight/2.0f;
		backgroundCircleX      = measuredWidth/2.0f;
		backgroundCircleRadius = Math.min(backgroundCircleX,backgroundCircleY);
		backgroundCircleRadius = backgroundCircleRadius - rimWidth;
		
		//Set up the outer ring. Note: some hard coded values need to be changed to variables in this code
		rimShaderOuter = new LinearGradient(0, 0, measuredWidth, measuredHeight, Color.WHITE, Color.BLUE, TileMode.MIRROR);
		
		rimPaintOuter.setAntiAlias(true);
		rimPaintOuter.setStyle(Style.STROKE);
		rimPaintOuter.setStrokeWidth(rimWidth-2);
		rimPaintOuter.setShader(rimShaderOuter);
		canvas.drawCircle(backgroundCircleX, backgroundCircleY, backgroundCircleRadius+(rimWidth/2), rimPaintOuter);
		
		rimShaderInner = new LinearGradient(0, 0, measuredWidth, measuredHeight, Color.BLUE, Color.WHITE, TileMode.MIRROR);
		
		rimPaintInner.setAntiAlias(true);
		rimPaintInner.setStyle(Style.STROKE);
		rimPaintInner.setStrokeWidth(6);
		rimPaintInner.setShader(rimShaderInner);
		canvas.drawCircle(backgroundCircleX, backgroundCircleY, backgroundCircleRadius+(1), rimPaintInner);
		
		
		rimPaintOuterLine.setAntiAlias(true);
		rimPaintOuterLine.setColor(Color.DKGRAY);
		rimPaintOuterLine.setStyle(Style.STROKE);
		rimPaintOuterLine.setStrokeWidth(2);
		canvas.drawCircle(backgroundCircleX, backgroundCircleY, backgroundCircleRadius+9, rimPaintOuterLine);
		canvas.drawCircle(backgroundCircleX, backgroundCircleY, backgroundCircleRadius, targetAreaBackGroundPaint);
		
		
		//draw the intial Joy Stick Handle
		if(joyStickX<=0 && joyStickY<=0) {
			joyStickShader = new RadialGradient(backgroundCircleX, backgroundCircleY, joyStickRadius, Color.DKGRAY, Color.BLUE, TileMode.CLAMP );
			joyStickHandlePaint.setShader(joyStickShader);
			canvas.drawCircle(backgroundCircleX,backgroundCircleY, joyStickRadius ,joyStickHandlePaint);
		}
		
		else {
			joyStickShader = new RadialGradient(joyStickX, joyStickY, joyStickRadius, Color.DKGRAY, Color.BLUE, TileMode.CLAMP );
			joyStickHandlePaint.setShader(joyStickShader);
			canvas.drawCircle(joyStickX, joyStickY, joyStickRadius, joyStickHandlePaint);
		}
		
		
		//Circle that allows you to visualize the area that is used as the touch area
		if(drawConstraintCircle){
			canvas.drawCircle(backgroundCircleX,backgroundCircleY, backgroundCircleRadius, constraintPaint);
		}
		
	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
		
		super.onLayout(changed, left, top, right, bottom);
		
		topDim    = top;
		bottomDim = bottom;
		rightDim  = right;
		leftDim   = left;
		
		measuredHeight = getMeasuredHeight();
		measuredWidth  = getMeasuredWidth();
		
		if(inDebug) { Log.d(TAG, "onLayout top: " + topDim + " bottom: " + bottomDim + " left: " + leftDim + " right: " + rightDim); } 
		
	}
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		
		if(inDebug){
			Log.d(TAG,"Width MeasureSpec " + MeasureSpec.toString(widthMeasureSpec));
			Log.d(TAG,"Height MeasureSpec " + MeasureSpec.toString(heightMeasureSpec));
		}
		
		//get the mode/size for the width and height measure specs
		int widthMode  = MeasureSpec.getMode(widthMeasureSpec);
		int widthSize  = MeasureSpec.getSize(widthMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);
		
		int determinedWidth  = determineDimension(widthMode, widthSize);
		int determinedHeight = determineDimension(heightMode, heightSize);
		
		//since the view needs to be symetric we need to take the smallest of 
		//height/width and make both view dimensions that
		int finalDimension = Math.min(determinedWidth, determinedHeight);
		
		//finalWidthHeight = 150;
		if(inDebug) { Log.d(TAG,"Final Height and Width dimension set to: " + finalDimension); }
		
		//lets divide the final dimension by two to see if it is even
		int isOddWidth = finalDimension%2;
		
		//lets adjust to keep the width/height symmetric
		if(isOddWidth==1){ 
			finalDimension--;
		}
		
		if(inDebug) { Log.d(TAG,"Final Height and Width dimension after adjustment set to: " + finalDimension); }
		
		setMeasuredDimension(finalDimension,finalDimension);
	} 

	
	//determine if parent request a size or use default
	private int determineDimension(int mode,int size){
		if(mode == MeasureSpec.AT_MOST|| mode == MeasureSpec.EXACTLY) {
			return size;
		}
		
		else {
			return getDefaultDim();
		}
	}
	
	//pass back the default dimension
	private int getDefaultDim() {
		return 250;
	}
	
	
	//onTouch related stuff
	public void setPointerId(int id) {
		this.pointerId = id;
	}
	
	public int getPointerId() {
		return pointerId;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
	    
		//get the action
		final int action = ev.getAction();
		
		//determine is 
		switch (action & MotionEvent.ACTION_MASK) {
		
			//we are moving
		    case MotionEvent.ACTION_MOVE: {
		    	
		    	if(inDebug) {  Log.d(TAG,"ACTION_MOVE "); }
		    	
		    	return processMoveEvent(ev);
		  
		    }	    
		    
		    //first pointer has been lifted up
		    case MotionEvent.ACTION_CANCEL: 
		    case MotionEvent.ACTION_UP: {
		    	
		    	//make sure we are a valid pointer
		    	if ( pointerId != INVALID_POINTER_ID ) {
		    		
		    		if(inDebug) { Log.d(TAG, "ACTION_UP"); }
		    		
			    	returnHandleToCenter();
		        	setPointerId(INVALID_POINTER_ID);
		        	return true;
		        	
		    	}
		        break;
		    }
		    
		    //second pointer has been lifted
		    case MotionEvent.ACTION_POINTER_UP: {
		    	
		    	//make sure we have a valid pointer
		    	if ( pointerId != INVALID_POINTER_ID ) {
		    		
			        final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
			        final int pointerId = ev.getPointerId(pointerIndex);
			        
			        //make sure the second pointer matches our current pointer
			        if ( pointerId == this.pointerId ) {
			        	
			        	if(inDebug) { Log.d(TAG, "ACTION_POINTER_UP: " + pointerId); }
			        	
			        	returnHandleToCenter();
			        	setPointerId(INVALID_POINTER_ID);
			    		return true;
			        }
		    	}
		        break;
		    }
		    
		    //first pointer is down
		    case MotionEvent.ACTION_DOWN: {
		    	
		    	//make sure we don't already have a pointer
		    	if ( pointerId == INVALID_POINTER_ID ) {
		    		
		    		int x = (int) ev.getX();
		    		//if ( x >= offsetX && x < offsetX + dimX ) {
			        	
		    			//set pointer one as our pointer
		    			setPointerId(ev.getPointerId(0));
			        	if(inDebug) { Log.d(TAG, "ACTION_DOWN: " + getPointerId()); }
			    		return true;
		    		//}
		    	}
		        break;
		    }
		    
		    //second pointer is down
		    case MotionEvent.ACTION_POINTER_DOWN: {
		    	if ( pointerId == INVALID_POINTER_ID ) {
			       
		    		final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
			        final int pointerId = ev.getPointerId(pointerIndex);
		    		int x = (int) ev.getX(pointerId);
		    		int y = (int) ev.getY(pointerId);
		    		
		    		//if ( x >= offsetX && x < offsetX + dimX ) {
		    		if(inDebug) { Log.d(TAG, "ACTION_POINTER_DOWN: " + pointerId + " x " + x + " y " + y); }
			        	setPointerId(pointerId);
			    		return true;
		    		//}
		    	}
		        break;
		    }
	    }
		return false;
	}
	
	private boolean processMoveEvent(MotionEvent ev) {
		if ( pointerId != INVALID_POINTER_ID ) {
			
			final int pointerIndex = ev.findPointerIndex(pointerId);
			
			// Translate touch position to center of view
			float x = ev.getX(pointerIndex);
			float touchX = normalizeX(x);
			float y = ev.getY(pointerIndex);
			float touchY = normalizeY(y);
			double radius = Math.sqrt((touchX*touchX)+(touchY*touchY));
			Log.d(TAG,"Radius " + radius + " background " + backgroundCircleRadius);
			
			if(radius<backgroundCircleRadius-joyStickRadius*.55){
			//if((x>=rimWidth && x<measuredWidth-rimWidth) && (y>=rimWidth && y<measuredHeight-rimWidth)) {
				
				if(inDebug) { Log.d(TAG, String.format("ACTION_MOVE: %d (%03.0f, %03.0f) => (%03.0f, %03.0f) rimWidth %f %d %d", pointerIndex, x, y, touchX, touchY, rimWidth, measuredWidth, measuredHeight)); }
				
			    //reportOnMoved();
				joyStickX = x;
				joyStickY = y;
				invalidate();
			
				//touchPressure = ev.getPressure(pointerIndex);
				//reportOnPressure();
			
				return true;
			}
			else {
				if(inDebug) { Log.d(TAG, "Out of range x " + x + " y " + y); }
				returnHandleToCenter();
				return false;
			}
		}
		return false;
	}
	
	private float normalizeY(float y) {
		Log.d(TAG,"Normalize y " + backgroundCircleY + " " + y);
		if(y>backgroundCircleY){
			return (backgroundCircleY-y);
		}
		else {
			return (y-backgroundCircleY);
		}
	}

	private float normalizeX(float x) {
		Log.d(TAG,"Normalize x " + backgroundCircleX + " " + x);
		if(x>backgroundCircleX){
			return (x-backgroundCircleX);
		}
		else {
			return (backgroundCircleX-x);
		}
	}

	private void returnHandleToCenter() {
		
		joyStickX = backgroundCircleX;
		joyStickY = backgroundCircleY;
		invalidate();
		
	/*	if (true ) {
			final int numberOfFrames = 5;
			final double intervalsX = (0 - touchX) / numberOfFrames;
			final double intervalsY = (0 - touchY) / numberOfFrames;

			for (int i = 0; i < numberOfFrames; i++) {
				final int j = i;
				postDelayed(new Runnable() {
					@Override
					public void run() {
						touchX += intervalsX;
						touchY += intervalsY;
						
						reportOnMoved();
						invalidate();
						
						if (moveListener != null && j == numberOfFrames - 1) {
							moveListener.OnReturnedToCenter();
						}
					}
				}, i * 40);
			}

			if (moveListener != null) {
				moveListener.OnReleased();
			}
		}*/
	}

	@Override
	protected void onRestoreInstanceState(Parcelable state) {
		super.onRestoreInstanceState(state);
	}

	@Override
	protected Parcelable onSaveInstanceState() {
		return super.onSaveInstanceState();
	}

}
